2. Data Preparation
When working with databases, we should always use packages that are DBI-compliant.
The odbc package provides a standard way for us to connect to any database as long as we have an ODBC driver installed. The odbc package is DBI-compliant, and is recommended for ODBC connections in this project.
The plotly package is loaded for data visualization.
2.1 Connect to the Database
# Load the DBI and odbc packages
library(DBI)
library(odbc)
# Verify that odbc recognizes the installed drivers using odbcListDrivers()
sort(unique(odbcListDrivers()[[1]]))
[1] "ODBC Driver 17 for SQL Server"
# check the available driver
odbcListDrivers(
keep = getOption("odbc.drivers_keep"),
filter = getOption("odbc.drivers_filter")
)
Only ODBC Driver 17 for SQL Server is available and we will use it for our connection.
# Pass the server address, database name, and the credentials to be used to the connection
con <- DBI::dbConnect(odbc(),
Driver = "ODBC Driver 17 for SQL Server",
Server = "aseaspdghrtrain.database.windows.net",
Database = "asdb-eas-pd-ghrtrain01",
UID = "RZ15912", # use your own userID
PWD = "D7!zUi*#", # use your own password
PORT = 1433)
# Check the connection
con
<OdbcConnection> RZ15912@aseaspdghrtrain
Database: asdb-eas-pd-ghrtrain01
Microsoft SQL Server Version: 12.00.2000
The connection is set up and we can specify the connection for all sql chunks in the following.
# Set up the connection for all sql chunks
knitr::opts_chunk$set(connection = "con")
Now test with a simply query and save the result to a variable.
-- Select all from offices table
SELECT * FROM offices
# Display the returned result
mydata
That worked and we can create the entity relationship diagram (ERD).
2.2 ERD
By checking all the tables in the database, I created below ERD by Lucidchart. The primary key of each table is highlighed in Red. The data type of each column is listed as well. For table orderDetails, it has composite primary keys orderNumber and productCode.

3. Query for the Business Problems
3.1 The sales of each product line
SELECT
p.productLine,
SUM(o.quantityOrdered * o.priceEach) AS totalSales,
Round (SUM(o.quantityOrdered * o.priceEach) / (SELECT SUM(quantityOrdered * priceEach) FROM orderdetails), 3) AS salesPct,
SUM(o.quantityOrdered) AS totalQuantity
FROM orderdetails AS o
LEFT JOIN products AS p
ON o.productCode = p.productCode
Group BY p.productLine
ORDER BY 2 DESC;
top_productLine
This table is what we want and we can proceed the data visualization for presentation.
# Plot the total sales percentage of different product lines
fig1 <- plot_ly(data = top_productLine, labels = ~productLine, values = ~totalSales,
type = 'pie',
text = ~totalSales,
textposition = 'inside',
textinfo = 'label+percent',
insidetextfont = list(color = '#FFFFFF'),
hovertemplate = "%{label}<br>Sales: %{text:,.0f}<br>Percentage: %{percent}<extra></extra>",
marker = list(colors = brewer.pal(n = 7, name = "Paired"),
line = list(color = '#FFFFFF', width = 1)),
showlegend = FALSE)
fig1 <- fig1 %>% layout(title = 'Total Sales Percentage by Different Product Lines',
xaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE),
yaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE))
fig1
Beside, we can plot the ordered quantity of each product line.
# Plot the ordered quantity of different product lines
fig2 <- plot_ly(data = top_productLine, y = ~reorder(productLine, totalQuantity),
x = ~totalQuantity,
type = "bar",
orientation = "h",
text = ~totalQuantity,
texttemplate = "%{x:,}",
textposition = "auto",
hovertemplate = "%{label}<br>Ordered Qty: %{text:,}<extra></extra>",
width = 700,
height = 400)
fig2 <- fig2 %>% layout(title = "Total Quantity Ordered by Different Product Lines",
yaxis = list(title = ""),
xaxis = list(title = "", showgrid = FALSE, showticklabels = FALSE))
fig2
Classic cars is the biggest product line by sales and quantity sold, accounting for about 40% of total sales. Vintage cars is the second biggest. Compared to other product lines, Trains is the much smaller product line, only accounting for about 2% of total sales.
3.2 The ordered and stock quantity of each product
We can use below query to generate the quantity sold and in stock for each product.
-- New query with ROLLUP clause, without using JOIN
SELECT
pl.productLine,
p.productName,
SUM(od.quantityOrdered) AS salesQuantity,
AVG(p.quantityInStock) AS stockQuantity
FROM
productlines AS pl,
products AS p,
orderdetails AS od
WHERE p.productLine = pl.productLine
AND p.productCode = od.productCode
GROUP BY pl.productLine, p.productName -- Use grouping function
ORDER BY 1, 3 DESC;
top_products
We can see that the company has a catalogue of 109 products. Now let’s make a scatter plot to visualize the table, with a filter function for different procut lines.
# plot the product sales and stock
fig3 <- plot_ly(data = top_products, x = ~salesQuantity, y = ~stockQuantity,
color = ~productLine, colors = "Paired",
type = "scatter",
mode = "markers",
text = ~productName,
hovertemplate = "%{text}<br>Order Qty: %{x}<br>Stock Qty: %{y}<extra></extra>",
width = 750,
height = 450,
# add a filter
transforms = list(
list(
type = 'filter',
target = ~productLine,
operation = '=',
value = unique(top_products$productLine))
))
# add dropdown list for the filter
fig3 <- fig3 %>%
layout(
title = "Sales and Stock of Products",
xaxis = list(title = "Sales Quantity"),
yaxis = list(title = "Stock Quantity"),
updatemenus = list(
list(
type = "dropdown",
active = 0,
yanchor = "bottom",
buttons = list(
list(method = "restyle",
args = list("transforms[0].value", unique(top_products$productLine)),
label = "All"),
list(method = "restyle",
args = list("transforms[0].value", unique(top_products$productLine)[1]),
label = unique(top_products$productLine)[1]),
list(method = "restyle",
args = list("transforms[0].value", unique(top_products$productLine)[2]),
label = unique(top_products$productLine)[2]),
list(method = "restyle",
args = list("transforms[0].value", unique(top_products$productLine)[3]),
label = unique(top_products$productLine)[3]),
list(method = "restyle",
args = list("transforms[0].value", unique(top_products$productLine)[4]),
label = unique(top_products$productLine)[4]),
list(method = "restyle",
args = list("transforms[0].value", unique(top_products$productLine)[5]),
label = unique(top_products$productLine)[5]),
list(method = "restyle",
args = list("transforms[0].value", unique(top_products$productLine)[6]),
label = unique(top_products$productLine)[6]),
list(method = "restyle",
args = list("transforms[0].value", unique(top_products$productLine)[7]),
label = unique(top_products$productLine)[7])
)))
)
fig3
We see an outlier! By checking the hovertext, we can know it is 1992 Ferrari 360 Spider Red. It is so popular as only this classic car has an ordered quantity over 1800, while all of the other products are sold with a number between 767 and 1111. Besides, we don’t have a product with very low ordered quantity.
By the filter, we can see that the Train product line only has 3 products. This is the contributing factor to its low sales.
In this plot, we can find the products with low stock (less than 200), they are:
- 1968 Mustang
- 1928 Ford Phaeton Deluxe
- 1997 BWM F650 ST
- 1960 BSA Gold Star DBD34
3.2 The sales of each sales manager and their subordinates
In employees table, each sales representative has a direct line manager to report to. I will create an SQL View to show the list of each manager and their subordinates.
CREATE VIEW supSub_rz AS
(
SELECT
e2.employeeNumber AS managerID,
CONCAT(e2.firstName, ' ', e2.lastName) AS managerName,
e2.jobTitle,
e1.employeeNumber AS subordinateID,
CONCAT(e1.firstName, ' ', e1.lastName) AS subordinateName
FROM employees AS e1, employees AS e2
WHERE e1.reportsTo = e2.employeeNumber
ORDER BY 1 OFFSET 0 ROWS
);
SELECT * FROM supSub_rz
That’s it! The company has 22 employees in total, 1 President, 1 VP Sales. 3 Regional Sales Manager and 1 Sales Rep directly report to the VP Sales. The four guys also have their subordinates reporting to them.
Now let’s write a query with OLAP function ROLLUP to return the sales number of each manager and their subordinates.
SELECT
s.managerName,
s.subordinateName,
SUM(od.quantityOrdered * priceEach) AS sales
FROM supSub_rz AS s, customers AS c, orders AS o, orderdetails AS od
WHERE s.subordinateID= c.salesRepEmployeeNumber
AND c.customerNumber = o.customerNumber
AND o.orderNumber = od.orderNumber
GROUP BY ROLLUP(s.managerName, s.subordinateName)
employee_sales
To have a clearer view for the management, we can add another column to show the sales rank under each sales manager, with window function RANK. According to the job title, we know that each sales manager is responsible for a region, such as NA, EMEA, APAC.
SELECT
s.managerName,
s.jobTitle,
s.subordinateName,
SUM(od.quantityOrdered * priceEach) AS sales,
RANK() OVER (PARTITION BY managerName ORDER BY SUM(od.quantityOrdered * priceEach) DESC) AS salesRank
FROM supSub_rz AS s, customers AS c, orders AS o, orderdetails AS od
WHERE s.subordinateID= c.salesRepEmployeeNumber
AND c.customerNumber = o.customerNumber
AND o.orderNumber = od.orderNumber
GROUP BY s.managerName, s.jobTitle, s.subordinateName
employee_rank
# Plot the sales of different sales reps
fig4 <- plot_ly(data = employee_rank, y = ~reorder(subordinateName, sales),
x = ~sales,
color = ~managerName,
type = "bar",
orientation = "h",
text = ~salesRank,
texttemplate = "%{text}",
textposition = "auto",
hovertemplate = "%{label}<br>Sales: %{x:,}<br>Region Rank: %{text}<extra></extra>",
width = 700,
height = 400)
fig4 <- fig4 %>% layout(title = "Employee Sales of Different Managers",
yaxis = list(title = ""),
xaxis = list(title = "", showgrid = TRUE, showticklabels = TRUE),
legend = list(x = 0.7, y = 0.2,
title = list(text = "<b> Line Manager </b>")))
fig4
In this plot, we can clearly see the sales of each sales rep. The sales of each sales rep vary a lot, in the range between 0.3M to over 1.2M. 4 out of the top 5 sales reps are from Gerard’s team (EMEA Region), the other 1 is from Anthony’s team (NA Region). The sales reps from William’s team (APAC Region) has median sales number, while Mary, the company’s VP Sales has Mami Nishi, who directly report to her.
LS0tCnRpdGxlOiAiTW9kZWwgS2l0IFNhbGVzIEFuYWx5c2lzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCiFbXShodHRwczovL3d3dy53b25kZXJsYW5kbW9kZWxzLmNvbS9tZWRpYS9yZXNvdXJjZXMvU2hvcFBpY3MvV29uZGVybGFuZCUyME1vZGVsJTIwRGVwdC0yJTIwMTItMDUtMTUuanBnKQoKIyAxLiBEaXNjb3ZlcnkKClRoaXMgYXJ0aWNsZSBpcyB0byBhbmFseXplIHRoZSBzYWxlcyBkYXRhIG9mIGEgbW9kZWwga2l0IHByb3ZpZGVyLiBUaGVpciBwcm9kdWN0IGxpbmUgY29uc2lzdHMgb2YgbW9kZWwgc2hpcHMsIHBsYW5lcywgY2FycyBhbmQgbWlsaXRhcnkgZ3JvdW5kIHZlaGljbGVzLiBJIHdpbGwgYWNjZXNzIHRoZSBkYXRhIGJ5IGNyZWF0aW5nIGEgY29ubmVjdGlvbiB0byB0aGUgZGF0YWJhc2UuCgpBcyB0aGUgc2FsZXMgZGF0YSBpbmNsdWRpbmcgdGhlIGluZm9ybWF0aW9uIG9mIGN1c3RvbWVycywgb3JkZXJzLCBwcm9kdWN0cywgcHJvZHVjdCBsaW5lcyBhbmQgdGhlIGNvbXBhbnkgZW1wbG95ZWVzLCBJIHdpbGwgYW5zd2VyICoqYmVsb3cgYnVzaW5lc3MgcHJvYmxlbXMqKiBpbiB0aGlzIGFydGljbGUsIHNvIHRoYXQgdGhlIG1hbmFnZW1lbnQgY291bGQgYmV0dGVyIHVuZGVyc3RhbmQgdGhlIGNvbXBhbnkncyBidXNpbmVzcyBzdGF0dXMuCgoxLiBIb3cgaXMgdGhlIHNhbGVzIG9mIGVhY2ggcHJvZHVjdCBsaW5lPwoKMi4gV2hhdCdzIHRoZSBvcmRlcmVkIHF1YW50aXR5IGFuZCBzdG9jayBxdWFudGl0eSBvZiBlYWNoIHByb2R1Y3Q/CgozLiBBcyB3ZSBoYXZlIHJlZ2lvbmFsIG1hbmFnZXJzLCB3aGF0J3MgdGhlIHNhbGVzIG9mIHRoZSBlbXBsb3llZXMgdW5kZXIgZWFjaCBtYW5hZ2VyPwoKSSB3aWxsIHVzZSBhZHZhbmNlZCBTUUwgZnVuY3Rpb25zIHN1Y2ggYXMgc3VicXVlcnksIE9MQVAsIHdpbmRvdyBmdW5jdGlvbiB0byBhbnN3ZXIuCgojIDIuIERhdGEgUHJlcGFyYXRpb24KCldoZW4gd29ya2luZyB3aXRoIGRhdGFiYXNlcywgd2Ugc2hvdWxkIGFsd2F5cyB1c2UgcGFja2FnZXMgdGhhdCBhcmUgREJJLWNvbXBsaWFudC4KClRoZSBgb2RiY2AgcGFja2FnZSBwcm92aWRlcyBhIHN0YW5kYXJkIHdheSBmb3IgdXMgdG8gY29ubmVjdCB0byBhbnkgZGF0YWJhc2UgYXMgbG9uZyBhcyB3ZSBoYXZlIGFuIE9EQkMgZHJpdmVyIGluc3RhbGxlZC4gVGhlIGBvZGJjYCBwYWNrYWdlIGlzIERCSS1jb21wbGlhbnQsIGFuZCBpcyByZWNvbW1lbmRlZCBmb3IgT0RCQyBjb25uZWN0aW9ucyBpbiB0aGlzIHByb2plY3QuCgpUaGUgYHBsb3RseWAgcGFja2FnZSBpcyBsb2FkZWQgZm9yIGRhdGEgdmlzdWFsaXphdGlvbi4KCiMjIDIuMSBDb25uZWN0IHRvIHRoZSBEYXRhYmFzZQpgYGB7cn0KIyBMb2FkIHRoZSBEQkkgYW5kIG9kYmMgcGFja2FnZXMKbGlicmFyeShEQkkpCmxpYnJhcnkob2RiYykKbGlicmFyeShwbG90bHkpICMgZm9yIHZpc3VhbGl6YXRpb24KCiMgVmVyaWZ5IHRoYXQgb2RiYyByZWNvZ25pemVzIHRoZSBpbnN0YWxsZWQgZHJpdmVycyB1c2luZyBvZGJjTGlzdERyaXZlcnMoKQpzb3J0KHVuaXF1ZShvZGJjTGlzdERyaXZlcnMoKVtbMV1dKSkKYGBgCgpgYGB7cn0KIyBjaGVjayB0aGUgYXZhaWxhYmxlIGRyaXZlcgpvZGJjTGlzdERyaXZlcnMoCiAga2VlcCA9IGdldE9wdGlvbigib2RiYy5kcml2ZXJzX2tlZXAiKSwKICBmaWx0ZXIgPSBnZXRPcHRpb24oIm9kYmMuZHJpdmVyc19maWx0ZXIiKQopCmBgYAoKT25seSBgT0RCQyBEcml2ZXIgMTcgZm9yIFNRTCBTZXJ2ZXJgIGlzIGF2YWlsYWJsZSBhbmQgd2Ugd2lsbCB1c2UgaXQgZm9yIG91ciBjb25uZWN0aW9uLgoKYGBge3J9CiMgUGFzcyB0aGUgc2VydmVyIGFkZHJlc3MsIGRhdGFiYXNlIG5hbWUsIGFuZCB0aGUgY3JlZGVudGlhbHMgdG8gYmUgdXNlZCB0byB0aGUgY29ubmVjdGlvbgpjb24gPC0gREJJOjpkYkNvbm5lY3Qob2RiYygpLAogICAgICAgICAgICAgICAgIERyaXZlciA9ICJPREJDIERyaXZlciAxNyBmb3IgU1FMIFNlcnZlciIsCiAgICAgICAgICAgICAgICAgU2VydmVyID0gImFzZWFzcGRnaHJ0cmFpbi5kYXRhYmFzZS53aW5kb3dzLm5ldCIsCiAgICAgICAgICAgICAgICAgRGF0YWJhc2UgPSAiYXNkYi1lYXMtcGQtZ2hydHJhaW4wMSIsCiAgICAgICAgICAgICAgICAgVUlEID0gIlJaMTU5MTIiLCAgIyB1c2UgeW91ciBvd24gdXNlcklECiAgICAgICAgICAgICAgICAgUFdEID0gIkQ3IXpVaSojIiwgIyB1c2UgeW91ciBvd24gcGFzc3dvcmQKICAgICAgICAgICAgICAgICBQT1JUID0gMTQzMykKCiMgQ2hlY2sgdGhlIGNvbm5lY3Rpb24KY29uCmBgYApUaGUgY29ubmVjdGlvbiBpcyBzZXQgdXAgYW5kIHdlIGNhbiBzcGVjaWZ5IHRoZSBjb25uZWN0aW9uIGZvciBhbGwgc3FsIGNodW5rcyBpbiB0aGUgZm9sbG93aW5nLgoKYGBge3Igc2V0dXB9CiMgU2V0IHVwIHRoZSBjb25uZWN0aW9uIGZvciBhbGwgc3FsIGNodW5rcwprbml0cjo6b3B0c19jaHVuayRzZXQoY29ubmVjdGlvbiA9ICJjb24iKQpgYGAKCk5vdyB0ZXN0IHdpdGggYSBzaW1wbHkgcXVlcnkgYW5kIHNhdmUgdGhlIHJlc3VsdCB0byBhIHZhcmlhYmxlLgpgYGB7c3FsLCBvdXRwdXQudmFyID0gIm15ZGF0YSJ9Ci0tIFNlbGVjdCBhbGwgZnJvbSBvZmZpY2VzIHRhYmxlClNFTEVDVCAqIApGUk9NIG9mZmljZXMKYGBgCgpgYGB7cn0KIyBEaXNwbGF5IHRoZSByZXR1cm5lZCByZXN1bHQKbXlkYXRhCmBgYAoKVGhhdCB3b3JrZWQgYW5kIHdlIGNhbiBjcmVhdGUgdGhlIGBlbnRpdHkgcmVsYXRpb25zaGlwIGRpYWdyYW0gKEVSRClgLgoKIyMgMi4yIEVSRApCeSBjaGVja2luZyBhbGwgdGhlIHRhYmxlcyBpbiB0aGUgZGF0YWJhc2UsIEkgY3JlYXRlZCBiZWxvdyBFUkQgYnkgW0x1Y2lkY2hhcnRdKGh0dHBzOi8vd3d3Lmx1Y2lkY2hhcnQuY29tL3BhZ2VzLykuIFRoZSBwcmltYXJ5IGtleSBvZiBlYWNoIHRhYmxlIGlzIGhpZ2hsaWdoZWQgaW4gKipSZWQqKi4gVGhlIGRhdGEgdHlwZSBvZiBlYWNoIGNvbHVtbiBpcyBsaXN0ZWQgYXMgd2VsbC4gRm9yIHRhYmxlIGBvcmRlckRldGFpbHNgLCBpdCBoYXMgY29tcG9zaXRlIHByaW1hcnkga2V5cyBgb3JkZXJOdW1iZXJgIGFuZCBgcHJvZHVjdENvZGVgLgoKIVtdKGh0dHBzOi8vZ2l0aHViLmNvbS9yaWNreXpoYW5nd2wvZGF0YV9hbmFseXRpY19wcm9qZWN0cy9ibG9iL21hc3Rlci9zcWwvZXJkLnBuZz9yYXc9dHJ1ZSkKCiMgMy4gUXVlcnkgZm9yIHRoZSBCdXNpbmVzcyBQcm9ibGVtcwojIyAzLjEgVGhlIHNhbGVzIG9mIGVhY2ggcHJvZHVjdCBsaW5lCgpgYGB7c3FsLCBvdXRwdXQudmFyID0gInRvcF9wcm9kdWN0TGluZSJ9ClNFTEVDVCAKICAgIHAucHJvZHVjdExpbmUsCiAgICBTVU0oby5xdWFudGl0eU9yZGVyZWQgKiBvLnByaWNlRWFjaCkgQVMgdG90YWxTYWxlcywKICAgIFJvdW5kIChTVU0oby5xdWFudGl0eU9yZGVyZWQgKiBvLnByaWNlRWFjaCkgLyAoU0VMRUNUIFNVTShxdWFudGl0eU9yZGVyZWQgKiBwcmljZUVhY2gpIEZST00gb3JkZXJkZXRhaWxzKSwgMykgQVMgc2FsZXNQY3QsIC0tIFVzZSBzdWJxdWVyeSwgYW5kIHVzZSBSb3VuZCBmdW5jdGlvbiB0byBzZXQgZGVjaW1hbAogICAgU1VNKG8ucXVhbnRpdHlPcmRlcmVkKSBBUyB0b3RhbFF1YW50aXR5CkZST00gb3JkZXJkZXRhaWxzIEFTIG8KTEVGVCBKT0lOIHByb2R1Y3RzIEFTIHAKT04gby5wcm9kdWN0Q29kZSA9IHAucHJvZHVjdENvZGUKR3JvdXAgQlkgcC5wcm9kdWN0TGluZQpPUkRFUiBCWSAyIERFU0M7CmBgYAoKCmBgYHtyfQojIGNoZWNrIHRoZSBxdWVyeSByZXN1bHQKdG9wX3Byb2R1Y3RMaW5lCmBgYAoKVGhpcyB0YWJsZSBpcyB3aGF0IHdlIHdhbnQgYW5kIHdlIGNhbiBwcm9jZWVkIHRoZSBkYXRhIHZpc3VhbGl6YXRpb24gZm9yIHByZXNlbnRhdGlvbi4KCmBgYHtyfQojIFBsb3QgdGhlIHRvdGFsIHNhbGVzIHBlcmNlbnRhZ2Ugb2YgZGlmZmVyZW50IHByb2R1Y3QgbGluZXMKZmlnMSA8LSBwbG90X2x5KGRhdGEgPSB0b3BfcHJvZHVjdExpbmUsIGxhYmVscyA9IH5wcm9kdWN0TGluZSwgdmFsdWVzID0gfnRvdGFsU2FsZXMsIAogICAgICAgICAgICAgICAgdHlwZSA9ICdwaWUnLAogICAgICAgICAgICAgICAgdGV4dCA9IH50b3RhbFNhbGVzLAogICAgICAgICAgICAgICAgdGV4dHBvc2l0aW9uID0gJ2luc2lkZScsCiAgICAgICAgICAgICAgICB0ZXh0aW5mbyA9ICdsYWJlbCtwZXJjZW50JywKICAgICAgICAgICAgICAgIGluc2lkZXRleHRmb250ID0gbGlzdChjb2xvciA9ICcjRkZGRkZGJyksCiAgICAgICAgICAgICAgICBob3ZlcnRlbXBsYXRlID0gIiV7bGFiZWx9PGJyPlNhbGVzOiAle3RleHQ6LC4wZn08YnI+UGVyY2VudGFnZTogJXtwZXJjZW50fTxleHRyYT48L2V4dHJhPiIsCiAgICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9ycyA9IGJyZXdlci5wYWwobiA9IDcsIG5hbWUgPSAiUGFpcmVkIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmUgPSBsaXN0KGNvbG9yID0gJyNGRkZGRkYnLCB3aWR0aCA9IDEpKSwKICAgICAgICAgICAgICAgIHNob3dsZWdlbmQgPSBGQUxTRSkKCmZpZzEgPC0gZmlnMSAlPiUgbGF5b3V0KHRpdGxlID0gJ1RvdGFsIFNhbGVzIFBlcmNlbnRhZ2UgYnkgRGlmZmVyZW50IFByb2R1Y3QgTGluZXMnLAogICAgICAgICB4YXhpcyA9IGxpc3Qoc2hvd2dyaWQgPSBGQUxTRSwgemVyb2xpbmUgPSBGQUxTRSwgc2hvd3RpY2tsYWJlbHMgPSBGQUxTRSksCiAgICAgICAgIHlheGlzID0gbGlzdChzaG93Z3JpZCA9IEZBTFNFLCB6ZXJvbGluZSA9IEZBTFNFLCBzaG93dGlja2xhYmVscyA9IEZBTFNFKSkKCmZpZzEKYGBgCgpCZXNpZGUsIHdlIGNhbiBwbG90IHRoZSBvcmRlcmVkIHF1YW50aXR5IG9mIGVhY2ggcHJvZHVjdCBsaW5lLgoKYGBge3J9CiMgUGxvdCB0aGUgb3JkZXJlZCBxdWFudGl0eSBvZiBkaWZmZXJlbnQgcHJvZHVjdCBsaW5lcwpmaWcyIDwtIHBsb3RfbHkoZGF0YSA9IHRvcF9wcm9kdWN0TGluZSwgeSA9IH5yZW9yZGVyKHByb2R1Y3RMaW5lLCB0b3RhbFF1YW50aXR5KSwgCiAgICAgICAgICAgICAgICB4ID0gfnRvdGFsUXVhbnRpdHksCiAgICAgICAgICAgICAgICB0eXBlID0gImJhciIsIAogICAgICAgICAgICAgICAgb3JpZW50YXRpb24gPSAiaCIsCiAgICAgICAgICAgICAgICB0ZXh0ID0gfnRvdGFsUXVhbnRpdHksCiAgICAgICAgICAgICAgICB0ZXh0dGVtcGxhdGUgPSAiJXt4Oix9IiwKICAgICAgICAgICAgICAgIHRleHRwb3NpdGlvbiA9ICJhdXRvIiwKICAgICAgICAgICAgICAgIGhvdmVydGVtcGxhdGUgPSAiJXtsYWJlbH08YnI+T3JkZXJlZCBRdHk6ICV7dGV4dDosfTxleHRyYT48L2V4dHJhPiIsCiAgICAgICAgICAgICAgICB3aWR0aCA9IDcwMCwKICAgICAgICAgICAgICAgIGhlaWdodCA9IDQwMCkKCmZpZzIgPC0gZmlnMiAlPiUgbGF5b3V0KHRpdGxlID0gIlRvdGFsIFF1YW50aXR5IE9yZGVyZWQgYnkgRGlmZmVyZW50IFByb2R1Y3QgTGluZXMiLAogICAgICAgICAgICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiIiksCiAgICAgICAgICAgICAgICAgICAgICAgIHhheGlzID0gbGlzdCh0aXRsZSA9ICIiLCBzaG93Z3JpZCA9IEZBTFNFLCBzaG93dGlja2xhYmVscyA9IEZBTFNFKSkKCmZpZzIKYGBgCgoqKkNsYXNzaWMgY2FycyoqIGlzIHRoZSBiaWdnZXN0IHByb2R1Y3QgbGluZSBieSBzYWxlcyBhbmQgcXVhbnRpdHkgc29sZCwgYWNjb3VudGluZyBmb3IgYWJvdXQgNDAlIG9mIHRvdGFsIHNhbGVzLiBWaW50YWdlIGNhcnMgaXMgdGhlIHNlY29uZCBiaWdnZXN0LiBDb21wYXJlZCB0byBvdGhlciBwcm9kdWN0IGxpbmVzLCAqKlRyYWlucyoqIGlzIHRoZSBtdWNoIHNtYWxsZXIgcHJvZHVjdCBsaW5lLCBvbmx5IGFjY291bnRpbmcgZm9yIGFib3V0IDIlIG9mIHRvdGFsIHNhbGVzLgoKIyMgMy4yIFRoZSBvcmRlcmVkIGFuZCBzdG9jayBxdWFudGl0eSBvZiBlYWNoIHByb2R1Y3QKCldlIGNhbiB1c2UgYmVsb3cgcXVlcnkgdG8gZ2VuZXJhdGUgdGhlIHF1YW50aXR5IHNvbGQgYW5kIGluIHN0b2NrIGZvciBlYWNoIHByb2R1Y3QuCgpgYGB7c3FsLCBvdXRwdXQudmFyID0gInRvcF9wcm9kdWN0cyJ9ClNFTEVDVCAKICAgIHBsLnByb2R1Y3RMaW5lLAogICAgcC5wcm9kdWN0TmFtZSwKICAgIFNVTShvZC5xdWFudGl0eU9yZGVyZWQpIEFTIHNhbGVzUXVhbnRpdHksCiAgICBBVkcocC5xdWFudGl0eUluU3RvY2spIEFTIHN0b2NrUXVhbnRpdHkKRlJPTSAKICAgIHByb2R1Y3RsaW5lcyBBUyBwbCwgCiAgICBwcm9kdWN0cyBBUyBwLCAKICAgIG9yZGVyZGV0YWlscyBBUyBvZApXSEVSRSBwLnByb2R1Y3RMaW5lID0gcGwucHJvZHVjdExpbmUKQU5EIHAucHJvZHVjdENvZGUgPSBvZC5wcm9kdWN0Q29kZQpHUk9VUCBCWSBwbC5wcm9kdWN0TGluZSwgcC5wcm9kdWN0TmFtZQpPUkRFUiBCWSAxLCAzIERFU0M7CmBgYAoKYGBge3J9CiMgY2hlY2sgdGhlIHJlc3VsdAp0b3BfcHJvZHVjdHMKYGBgCldlIGNhbiBzZWUgdGhhdCB0aGUgY29tcGFueSBoYXMgYSBjYXRhbG9ndWUgb2YgKioxMDkqKiBwcm9kdWN0cy4gTm93IGxldCdzIG1ha2UgYSBzY2F0dGVyIHBsb3QgdG8gdmlzdWFsaXplIHRoZSB0YWJsZSwgd2l0aCBhIGZpbHRlciBmdW5jdGlvbiBmb3IgZGlmZmVyZW50IHByb2N1dCBsaW5lcy4KCmBgYHtyfQojIHBsb3QgdGhlIHByb2R1Y3Qgc2FsZXMgYW5kIHN0b2NrCmZpZzMgPC0gcGxvdF9seShkYXRhID0gdG9wX3Byb2R1Y3RzLCB4ID0gfnNhbGVzUXVhbnRpdHksIHkgPSB+c3RvY2tRdWFudGl0eSwgCiAgICAgICAgICAgICAgIGNvbG9yID0gfnByb2R1Y3RMaW5lLCBjb2xvcnMgPSAiUGFpcmVkIiwKICAgICAgICAgICAgICAgdHlwZSA9ICJzY2F0dGVyIiwKICAgICAgICAgICAgICAgbW9kZSA9ICJtYXJrZXJzIiwKICAgICAgICAgICAgICAgdGV4dCA9IH5wcm9kdWN0TmFtZSwKICAgICAgICAgICAgICAgaG92ZXJ0ZW1wbGF0ZSA9ICIle3RleHR9PGJyPk9yZGVyIFF0eTogJXt4fTxicj5TdG9jayBRdHk6ICV7eX08ZXh0cmE+PC9leHRyYT4iLAogICAgICAgICAgICAgICB3aWR0aCA9IDc1MCwKICAgICAgICAgICAgICAgaGVpZ2h0ID0gNDUwLAogICAgICAgICAgICAgICAjIGFkZCBhIGZpbHRlcgogICAgICAgICAgICAgICB0cmFuc2Zvcm1zID0gbGlzdCgKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaXN0KAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICdmaWx0ZXInLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGFyZ2V0ID0gfnByb2R1Y3RMaW5lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3BlcmF0aW9uID0gJz0nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSB1bmlxdWUodG9wX3Byb2R1Y3RzJHByb2R1Y3RMaW5lKSkKICAgICAgICAgICAgICAgKSkKIyBhZGQgZHJvcGRvd24gbGlzdCBmb3IgdGhlIGZpbHRlcgpmaWczIDwtIGZpZzMgJT4lIAogICAgICAgICAgICBsYXlvdXQoCiAgICAgICAgICAgICAgdGl0bGUgPSAiU2FsZXMgYW5kIFN0b2NrIG9mIFByb2R1Y3RzIiwKICAgICAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiU2FsZXMgUXVhbnRpdHkiKSwKICAgICAgICAgICAgICB5YXhpcyA9IGxpc3QodGl0bGUgPSAiU3RvY2sgUXVhbnRpdHkiKSwKICAgICAgICAgICAgICB1cGRhdGVtZW51cyA9IGxpc3QoCiAgICAgICAgICAgICAgbGlzdCgKICAgICAgICAgICAgICAgIHR5cGUgPSAiZHJvcGRvd24iLAogICAgICAgICAgICAgICAgYWN0aXZlID0gMCwKICAgICAgICAgICAgICAgIHlhbmNob3IgPSAiYm90dG9tIiwKICAgICAgICAgICAgICAgIGJ1dHRvbnMgPSBsaXN0KAogICAgICAgICAgICAgICAgICBsaXN0KG1ldGhvZCA9ICJyZXN0eWxlIiwKICAgICAgICAgICAgICAgICAgICAgICBhcmdzID0gbGlzdCgidHJhbnNmb3Jtc1swXS52YWx1ZSIsIHVuaXF1ZSh0b3BfcHJvZHVjdHMkcHJvZHVjdExpbmUpKSwKICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9ICJBbGwiKSwKICAgICAgICAgICAgICAgICAgbGlzdChtZXRob2QgPSAicmVzdHlsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgYXJncyA9IGxpc3QoInRyYW5zZm9ybXNbMF0udmFsdWUiLCB1bmlxdWUodG9wX3Byb2R1Y3RzJHByb2R1Y3RMaW5lKVsxXSksCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSB1bmlxdWUodG9wX3Byb2R1Y3RzJHByb2R1Y3RMaW5lKVsxXSksCiAgICAgICAgICAgICAgICAgIGxpc3QobWV0aG9kID0gInJlc3R5bGUiLAogICAgICAgICAgICAgICAgICAgICAgIGFyZ3MgPSBsaXN0KCJ0cmFuc2Zvcm1zWzBdLnZhbHVlIiwgdW5pcXVlKHRvcF9wcm9kdWN0cyRwcm9kdWN0TGluZSlbMl0pLAogICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gdW5pcXVlKHRvcF9wcm9kdWN0cyRwcm9kdWN0TGluZSlbMl0pLAogICAgICAgICAgICAgICAgICBsaXN0KG1ldGhvZCA9ICJyZXN0eWxlIiwKICAgICAgICAgICAgICAgICAgICAgICBhcmdzID0gbGlzdCgidHJhbnNmb3Jtc1swXS52YWx1ZSIsIHVuaXF1ZSh0b3BfcHJvZHVjdHMkcHJvZHVjdExpbmUpWzNdKSwKICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHVuaXF1ZSh0b3BfcHJvZHVjdHMkcHJvZHVjdExpbmUpWzNdKSwKICAgICAgICAgICAgICAgICAgbGlzdChtZXRob2QgPSAicmVzdHlsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgYXJncyA9IGxpc3QoInRyYW5zZm9ybXNbMF0udmFsdWUiLCB1bmlxdWUodG9wX3Byb2R1Y3RzJHByb2R1Y3RMaW5lKVs0XSksCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSB1bmlxdWUodG9wX3Byb2R1Y3RzJHByb2R1Y3RMaW5lKVs0XSksCiAgICAgICAgICAgICAgICAgIGxpc3QobWV0aG9kID0gInJlc3R5bGUiLAogICAgICAgICAgICAgICAgICAgICAgIGFyZ3MgPSBsaXN0KCJ0cmFuc2Zvcm1zWzBdLnZhbHVlIiwgdW5pcXVlKHRvcF9wcm9kdWN0cyRwcm9kdWN0TGluZSlbNV0pLAogICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gdW5pcXVlKHRvcF9wcm9kdWN0cyRwcm9kdWN0TGluZSlbNV0pLAogICAgICAgICAgICAgICAgICBsaXN0KG1ldGhvZCA9ICJyZXN0eWxlIiwKICAgICAgICAgICAgICAgICAgICAgICBhcmdzID0gbGlzdCgidHJhbnNmb3Jtc1swXS52YWx1ZSIsIHVuaXF1ZSh0b3BfcHJvZHVjdHMkcHJvZHVjdExpbmUpWzZdKSwKICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IHVuaXF1ZSh0b3BfcHJvZHVjdHMkcHJvZHVjdExpbmUpWzZdKSwKICAgICAgICAgICAgICAgICAgbGlzdChtZXRob2QgPSAicmVzdHlsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgYXJncyA9IGxpc3QoInRyYW5zZm9ybXNbMF0udmFsdWUiLCB1bmlxdWUodG9wX3Byb2R1Y3RzJHByb2R1Y3RMaW5lKVs3XSksCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSB1bmlxdWUodG9wX3Byb2R1Y3RzJHByb2R1Y3RMaW5lKVs3XSkKICAgICAgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSkKICAgICAgKQoKZmlnMwpgYGAKCldlIHNlZSBhbiBvdXRsaWVyISBCeSBjaGVja2luZyB0aGUgaG92ZXJ0ZXh0LCB3ZSBjYW4ga25vdyBpdCBpcyAqKjE5OTIgRmVycmFyaSAzNjAgU3BpZGVyIFJlZCoqLiBJdCBpcyBzbyBwb3B1bGFyIGFzIG9ubHkgdGhpcyBjbGFzc2ljIGNhciBoYXMgYW4gb3JkZXJlZCBxdWFudGl0eSBvdmVyIDE4MDAsIHdoaWxlIGFsbCBvZiB0aGUgb3RoZXIgcHJvZHVjdHMgYXJlIHNvbGQgd2l0aCBhIG51bWJlciBiZXR3ZWVuIDc2NyBhbmQgMTExMS4gQmVzaWRlcywgd2UgZG9uJ3QgaGF2ZSBhIHByb2R1Y3Qgd2l0aCB2ZXJ5IGxvdyBvcmRlcmVkIHF1YW50aXR5LgoKQnkgdGhlIGZpbHRlciwgd2UgY2FuIHNlZSB0aGF0IHRoZSAqKlRyYWluKiogcHJvZHVjdCBsaW5lIG9ubHkgaGFzIDMgcHJvZHVjdHMuIFRoaXMgaXMgdGhlIGNvbnRyaWJ1dGluZyBmYWN0b3IgdG8gaXRzIGxvdyBzYWxlcy4KCkluIHRoaXMgcGxvdCwgd2UgY2FuIGZpbmQgdGhlIHByb2R1Y3RzIHdpdGggKipsb3cgc3RvY2sqKiAobGVzcyB0aGFuIDIwMCksIHRoZXkgYXJlOiAKCjEuIDE5NjggTXVzdGFuZwoyLiAxOTI4IEZvcmQgUGhhZXRvbiBEZWx1eGUKMy4gMTk5NyBCV00gRjY1MCBTVAo0LiAxOTYwIEJTQSBHb2xkIFN0YXIgREJEMzQKCiMjIDMuMiBUaGUgc2FsZXMgb2YgZWFjaCBzYWxlcyBtYW5hZ2VyIGFuZCB0aGVpciBzdWJvcmRpbmF0ZXMKCkluIGBlbXBsb3llZXNgIHRhYmxlLCBlYWNoIHNhbGVzIHJlcHJlc2VudGF0aXZlIGhhcyBhIGRpcmVjdCBsaW5lIG1hbmFnZXIgdG8gcmVwb3J0IHRvLiBJIHdpbGwgY3JlYXRlIGFuIFNRTCBWaWV3IHRvIHNob3cgdGhlIGxpc3Qgb2YgZWFjaCBtYW5hZ2VyIGFuZCB0aGVpciBzdWJvcmRpbmF0ZXMuCmBgYHtzcWx9Ci0tIGNyZWF0ZSBhIHNxbCB2aWV3CkNSRUFURSBWSUVXIHN1cFN1Yl9yeiBBUwogICAgKAogICAgU0VMRUNUCiAgICAgICAgZTIuZW1wbG95ZWVOdW1iZXIgQVMgbWFuYWdlcklELAogICAgICAgIENPTkNBVChlMi5maXJzdE5hbWUsICcgJywgZTIubGFzdE5hbWUpIEFTIG1hbmFnZXJOYW1lLAogICAgICAgIGUyLmpvYlRpdGxlLAogICAgICAgIGUxLmVtcGxveWVlTnVtYmVyIEFTIHN1Ym9yZGluYXRlSUQsCiAgICAgICAgQ09OQ0FUKGUxLmZpcnN0TmFtZSwgJyAnLCBlMS5sYXN0TmFtZSkgQVMgc3Vib3JkaW5hdGVOYW1lCiAgICBGUk9NIGVtcGxveWVlcyBBUyBlMSwgZW1wbG95ZWVzIEFTIGUyCiAgICBXSEVSRSBlMS5yZXBvcnRzVG8gPSBlMi5lbXBsb3llZU51bWJlcgogICAgT1JERVIgQlkgMSBPRkZTRVQgMCBST1dTCiAgICApOwpgYGAKCgpgYGB7c3FsfQotLSBjaGVjayB0aGUgY3JlYXRlZCB2aWV3ClNFTEVDVCAqIApGUk9NIHN1cFN1Yl9yegpgYGAKVGhhdCdzIGl0ISBUaGUgY29tcGFueSBoYXMgMjIgZW1wbG95ZWVzIGluIHRvdGFsLCAxIFByZXNpZGVudCwgMSBWUCBTYWxlcy4gMyBSZWdpb25hbCBTYWxlcyBNYW5hZ2VyIGFuZCAxIFNhbGVzIFJlcCBkaXJlY3RseSByZXBvcnQgdG8gdGhlIFZQIFNhbGVzLiBUaGUgZm91ciBndXlzIGFsc28gaGF2ZSB0aGVpciBzdWJvcmRpbmF0ZXMgcmVwb3J0aW5nIHRvIHRoZW0uCgpOb3cgbGV0J3Mgd3JpdGUgYSBxdWVyeSB3aXRoIE9MQVAgZnVuY3Rpb24gYFJPTExVUGAgdG8gcmV0dXJuIHRoZSBzYWxlcyBudW1iZXIgb2YgZWFjaCBtYW5hZ2VyIGFuZCB0aGVpciBzdWJvcmRpbmF0ZXMuCgpgYGB7c3FsLCBvdXRwdXQudmFyID0gImVtcGxveWVlX3NhbGVzIn0KU0VMRUNUCiAgICBzLm1hbmFnZXJOYW1lLAogICAgcy5zdWJvcmRpbmF0ZU5hbWUsCiAgICBTVU0ob2QucXVhbnRpdHlPcmRlcmVkICogcHJpY2VFYWNoKSBBUyBzYWxlcwpGUk9NIHN1cFN1Yl9yeiBBUyBzLCBjdXN0b21lcnMgQVMgYywgb3JkZXJzIEFTIG8sIG9yZGVyZGV0YWlscyBBUyBvZApXSEVSRSBzLnN1Ym9yZGluYXRlSUQ9IGMuc2FsZXNSZXBFbXBsb3llZU51bWJlcgpBTkQgYy5jdXN0b21lck51bWJlciA9IG8uY3VzdG9tZXJOdW1iZXIKQU5EIG8ub3JkZXJOdW1iZXIgPSBvZC5vcmRlck51bWJlcgpHUk9VUCBCWSBST0xMVVAocy5tYW5hZ2VyTmFtZSwgcy5zdWJvcmRpbmF0ZU5hbWUpIC0tIFVzZSBST0xMVVAgaGVyZQpgYGAKCmBgYHtyfQojIGNoZWNrIHRoZSByZXN1bHQKZW1wbG95ZWVfc2FsZXMKYGBgClRvIGhhdmUgYSBjbGVhcmVyIHZpZXcgZm9yIHRoZSBtYW5hZ2VtZW50LCB3ZSBjYW4gYWRkIGFub3RoZXIgY29sdW1uIHRvIHNob3cgdGhlIHNhbGVzIHJhbmsgdW5kZXIgZWFjaCBzYWxlcyBtYW5hZ2VyLCB3aXRoIHdpbmRvdyBmdW5jdGlvbiBgUkFOS2AuIEFjY29yZGluZyB0byB0aGUgam9iIHRpdGxlLCB3ZSBrbm93IHRoYXQgZWFjaCBzYWxlcyBtYW5hZ2VyIGlzIHJlc3BvbnNpYmxlIGZvciBhIHJlZ2lvbiwgc3VjaCBhcyBOQSwgRU1FQSwgQVBBQy4KCmBgYHtzcWwsIG91dHB1dC52YXIgPSAiZW1wbG95ZWVfcmFuayJ9ClNFTEVDVAogICAgcy5tYW5hZ2VyTmFtZSwKICAgIHMuam9iVGl0bGUsCiAgICBzLnN1Ym9yZGluYXRlTmFtZSwKICAgIFNVTShvZC5xdWFudGl0eU9yZGVyZWQgKiBwcmljZUVhY2gpIEFTIHNhbGVzLAogICAgUkFOSygpIE9WRVIgKFBBUlRJVElPTiBCWSBtYW5hZ2VyTmFtZSBPUkRFUiBCWSBTVU0ob2QucXVhbnRpdHlPcmRlcmVkICogcHJpY2VFYWNoKSBERVNDKSBBUyBzYWxlc1JhbmsgLS0gd2luZG93IGZ1bmN0aW9uCkZST00gc3VwU3ViX3J6IEFTIHMsIGN1c3RvbWVycyBBUyBjLCBvcmRlcnMgQVMgbywgb3JkZXJkZXRhaWxzIEFTIG9kCldIRVJFIHMuc3Vib3JkaW5hdGVJRD0gYy5zYWxlc1JlcEVtcGxveWVlTnVtYmVyCkFORCBjLmN1c3RvbWVyTnVtYmVyID0gby5jdXN0b21lck51bWJlcgpBTkQgby5vcmRlck51bWJlciA9IG9kLm9yZGVyTnVtYmVyCkdST1VQIEJZIHMubWFuYWdlck5hbWUsIHMuam9iVGl0bGUsIHMuc3Vib3JkaW5hdGVOYW1lCmBgYAoKYGBge3J9CmVtcGxveWVlX3JhbmsKYGBgCgpgYGB7cn0KIyBQbG90IHRoZSBzYWxlcyBvZiBkaWZmZXJlbnQgc2FsZXMgcmVwcwpmaWc0IDwtIHBsb3RfbHkoZGF0YSA9IGVtcGxveWVlX3JhbmssIHkgPSB+cmVvcmRlcihzdWJvcmRpbmF0ZU5hbWUsIHNhbGVzKSwKICAgICAgICAgICAgICAgIHggPSB+c2FsZXMsCiAgICAgICAgICAgICAgICBjb2xvciA9IH5tYW5hZ2VyTmFtZSwKICAgICAgICAgICAgICAgIHR5cGUgPSAiYmFyIiwgCiAgICAgICAgICAgICAgICBvcmllbnRhdGlvbiA9ICJoIiwKICAgICAgICAgICAgICAgIHRleHQgPSB+c2FsZXNSYW5rLAogICAgICAgICAgICAgICAgdGV4dHRlbXBsYXRlID0gIiV7dGV4dH0iLAogICAgICAgICAgICAgICAgdGV4dHBvc2l0aW9uID0gImF1dG8iLAogICAgICAgICAgICAgICAgaG92ZXJ0ZW1wbGF0ZSA9ICIle2xhYmVsfTxicj5TYWxlczogJXt4Oix9PGJyPlJlZ2lvbiBSYW5rOiAle3RleHR9PGV4dHJhPjwvZXh0cmE+IiwKICAgICAgICAgICAgICAgIHdpZHRoID0gNzAwLAogICAgICAgICAgICAgICAgaGVpZ2h0ID0gNDAwKQoKZmlnNCA8LSBmaWc0ICU+JSBsYXlvdXQodGl0bGUgPSAiRW1wbG95ZWUgU2FsZXMgb2YgRGlmZmVyZW50IE1hbmFnZXJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgeWF4aXMgPSBsaXN0KHRpdGxlID0gIiIpLAogICAgICAgICAgICAgICAgICAgICAgICB4YXhpcyA9IGxpc3QodGl0bGUgPSAiIiwgc2hvd2dyaWQgPSBUUlVFLCBzaG93dGlja2xhYmVscyA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQgPSBsaXN0KHggPSAwLjcsIHkgPSAwLjIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlID0gbGlzdCh0ZXh0ID0gIjxiPiBMaW5lIE1hbmFnZXIgPC9iPiIpKSkKCmZpZzQKYGBgCgpJbiB0aGlzIHBsb3QsIHdlIGNhbiBjbGVhcmx5IHNlZSB0aGUgc2FsZXMgb2YgZWFjaCBzYWxlcyByZXAuIFRoZSBzYWxlcyBvZiBlYWNoIHNhbGVzIHJlcCB2YXJ5IGEgbG90LCBpbiB0aGUgcmFuZ2UgYmV0d2VlbiAwLjNNIHRvIG92ZXIgMS4yTS4gNCBvdXQgb2YgdGhlIHRvcCA1IHNhbGVzIHJlcHMgYXJlIGZyb20gR2VyYXJkJ3MgdGVhbSAoRU1FQSBSZWdpb24pLCB0aGUgb3RoZXIgMSBpcyBmcm9tIEFudGhvbnkncyB0ZWFtIChOQSBSZWdpb24pLiBUaGUgc2FsZXMgcmVwcyBmcm9tIFdpbGxpYW0ncyB0ZWFtIChBUEFDIFJlZ2lvbikgaGFzIG1lZGlhbiBzYWxlcyBudW1iZXIsIHdoaWxlIE1hcnksIHRoZSBjb21wYW55J3MgVlAgU2FsZXMgaGFzIE1hbWkgTmlzaGksIHdobyBkaXJlY3RseSByZXBvcnQgdG8gaGVyLgoKIyA0LiBDb25jbHVzaW9uCgpGcm9tIHRoZSBhYm92ZSBhbmFseXNpcywgd2UgY2FuIGNvbmNsdWRlIHRoYXQ6CgoxLiBDbGFzc2ljIGNhcnMgYW5kIFZpbnRhZ2UgY2FycyBhcmUgdGhlIGNvbXBhbnkncyBiaWdnZXN0IHByb2R1Y3QgbGluZXMgYXMgYm90aCBoYXZlIG11Y2ggbW9yZSBzYWxlcyB0aGFuIG90aGVycy4gVHJhaW5zIGlzIHRoZSBzbWFsbGVzdCBvbmUsIGxhcmdlbHkgYmVjYXVzZSBvZiB0aGUgbG93IG51bWJlciBvZiBhdmFpbGFibGUgcHJvZHVjdHMuIFRoZSBjb21wYW55IHNob3VsZCBrZWVwIG1haW50YWluaW5nIHRoZSBnb29kIHNhbGVzIG9mIGNhciBwcm9kdWN0cy4gRGV2ZWxvcGluZyBtb3JlIHByb2R1Y3RzIGluIFRyYWlucyBjYW4gcHJvYmFibHkgaW5jcmVhc2UgdGhlIHNhbGVzIG9mIHRoaXMgcHJvZHVjdCBsaW5lLgoKMi4gVGhlIDE5OTIgRmVycmFyaSAzNjAgU3BpZGVyIFJlZCBpcyB0aGUgY29tcGFueSdzIGZsYWdzaGlwIHByb2R1Y3QuIFRoZSBjb21wYW55IGNhbiBtYWtlIGZ1cnRoZXIgYW5hbHlzaXMgb24gdGhlIHN1Y2Nlc3Mgb2YgdGhpcyBwcm9kdWN0LiBUaGUgZXhwZXJpZW5jZSBhbmQgbWFya2V0aW5nIHN0cmF0ZWd5IGlzIHBvc3NpYmxlIHRvIGJlIGFwcGxpZWQgdG8gb3RoZXIgcHJvZHVjdHMuIFRoZSBjb21wYW55IHNob3VsZCBwYXkgYXR0ZW50aW9uIHRvIHRoZSA0IGxvdy1zdG9jayBwcm9kdWN0cywgc3RvY2sgcmVwbGVuaXNobWVudCBpcyBuZWNlc3NhcnkgaW4gY2FzZSB0aGV5IGNhbm5vdCBmdWxmaWxsIG9yZGVycyBpbiB0aGUgbmVhciBmdXR1cmUuCgozLiBUaGUgY29tcGFueSdzIHRvcCBzYWxlcyByZXBzIGFyZSBhbG1vc3QgZm9yIHRoZSBFTUVBIGFuZCBOQSByZWdpb24uIEJ1dCBzb21lIHNhbGVzIHdpdGggdGhlIGxvd2VzdCBzYWxlcyBudW1iZXIgYXJlIGFsc28gaW4gdGhlIHR3byByZWdpb25zLiBUaGUgY29tcGFueSBjYW4gZnVydGhlciBhbmFseXplIHRoZSBzYWxlcyBkZXRhaWwgb2YgZWFjaCBzYWxlcyByZXAgdG8gYmV0dGVyIHVuZGVyc3RhbmQgdGhlaXIgcGVyZm9ybWFuY2UuCgo=